package cn.com.smartenergyplatform.account.service.impl;

import cn.com.smartenergyplatform.account.api.dto.WeChatPayDto;
import cn.com.smartenergyplatform.account.dao.entity.SepRechargeOrder;
import cn.com.smartenergyplatform.account.dao.entity.SepRefundRecord;
import cn.com.smartenergyplatform.account.dao.entity.SepWxConfig;
import cn.com.smartenergyplatform.account.dao.mapper.SepRefundRecordMapper;
import cn.com.smartenergyplatform.account.dao.mapper.SepWxConfigMapper;
import cn.com.smartenergyplatform.account.service.SepRechargeOrderService;
import cn.com.smartenergyplatform.account.service.WeChatPayService;
import cn.com.smartenergyplatform.account.utils.OrderUtil;
import cn.com.smartenergyplatform.account.utils.WeChatPayUtil;
import cn.com.smartenergyplatform.account.utils.core.dto.WeChatCallBackDTO;
import cn.com.smartenergyplatform.account.utils.core.dto.WeChatUrlDTO;
import cn.com.smartenergyplatform.account.utils.core.enums.DeviceType;
import cn.com.smartenergyplatform.account.utils.core.enums.WeChatPayType;
import cn.com.smartenergyplatform.common.core.domain.AjaxResult;
import cn.com.smartenergyplatform.common.exception.ServiceException;
import cn.com.smartenergyplatformbusiness.api.dto.SepRechargeOrderDTO;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.wechat.pay.contrib.apache.httpclient.exception.NotFoundException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * 作者: wx
 * 创建时间: 2025/3/25 11:44 星期二
 * 备注:
 */
@Service
@Slf4j
@Transactional(rollbackFor = Exception.class)
public class WeChatPayServiceImpl implements WeChatPayService {

    @Resource
    private SepWxConfigMapper sepWxConfigMapper;

    @Resource
    private SepRechargeOrderService rechargeOrderService;

    @Resource
    private SepRefundRecordMapper sepRefundRecordMapper;

    @Override
    public AjaxResult getWxOpenId(WeChatPayDto dto) {
        if (StrUtil.isBlank(dto.getCode())) {
            return AjaxResult.error("code不能为空");
        }

        //获取微信公众号配置信息
        //目前数据库只有一条信息
        SepWxConfig sepWxConfig = getLatestWxConfig();

        if (ObjectUtil.isEmpty(sepWxConfig)) {
            return AjaxResult.error("微信配置信息不存在");
        }
        String param = "?appid=" + sepWxConfig.getAppId() +
                "&secret=" + sepWxConfig.getAppSecret() +
                "&code=" + dto.getCode() +
                "&grant_type=authorization_code";
        String s = HttpUtil.get(WeChatUrlDTO.OAUTH2_URL + param);
        JSONObject jsonObject = JSONObject.parseObject(s);
        Object appid = jsonObject.get("appid");
        if (ObjectUtil.isEmpty(appid)) {
            return AjaxResult.error("获取openId失败,微信错误码:"
                    + jsonObject.get("code")
                    + "微信消息返回:" + jsonObject.get("message"));
        } else {
            return AjaxResult.success("操作成功", appid.toString());
        }
    }

    @Override
    public AjaxResult pay(WeChatPayDto dto) {
        // 校验充值金额
        if (StrUtil.isBlank(dto.getOpenId())) {
            return AjaxResult.error("openId不能为空");
        }
        BigDecimal dtoAmount = dto.getAmount();
        if (ObjectUtil.isEmpty(dtoAmount) || dtoAmount.compareTo(BigDecimal.ZERO) <= 0) {
            return AjaxResult.error("充值金额必须为正数");
        }

        // 校验用户ID
        if (StrUtil.isBlank(dto.getUserId())) {
            throw new ServiceException("用户Id不能为空");
        }
        log.info("===============发起微信支付API调用");
        // 解析支付类型
        DeviceType deviceType = DeviceType.getDeviceType(dto.getPayMethod());

        // 查询基础配置信息
        SepWxConfig sepWxConfig = getLatestWxConfig();

        if (ObjectUtil.isEmpty(sepWxConfig)) {
            return AjaxResult.error("微信配置信息不存在");
        }

        // 转换金额为分
        try {
            int amount = dtoAmount.multiply(new BigDecimal(100)).intValueExact();
            if (amount <= 0) {
                return AjaxResult.error("充值金额转换失败");
            }

            // 生成充值订单
            SepRechargeOrderDTO rechargeOrderDTO = new SepRechargeOrderDTO();
            rechargeOrderDTO.setUserId(dto.getUserId());
            rechargeOrderDTO.setTotalAmount(dtoAmount);
            rechargeOrderDTO.setPayType(1);

            AjaxResult addResult = rechargeOrderService.add(rechargeOrderDTO);
            if (addResult.isError()) {
                return AjaxResult.error("生成充值订单失败,原因:" + addResult.get("msg"));
            }

            SepRechargeOrderDTO order =
                    JSON.parseObject(addResult.get("data").toString(),
                            SepRechargeOrderDTO.class);

            // 构建支付参数
            Map<String, Object> params = buildPayParams(sepWxConfig,
                    order,
                    amount,
                    dto.getOpenId(),
                    dto.getIp(),
                    deviceType);

            // 发起支付请求
            JSONObject jsonObject = WeChatPayUtil.sendPostWeChatPay(sepWxConfig.getMchId(),
                    sepWxConfig.getMchSerialNo(),
                    sepWxConfig.getApiV3Key(),
                    sepWxConfig.getPrivateKeyPath(),
                    JSON.toJSONString(params),
                    sendWeChatPay(deviceType));

            // 处理支付回调
            WeChatCallBackDTO weChatCallBackDTO = handlePaymentResponse(
                    jsonObject,
                    deviceType,
                    sepWxConfig,
                    order.getOrderNo());
            if (null == weChatCallBackDTO) {
                return AjaxResult.error("微信支付发起失败,原因:" + jsonObject.getString("message"));
            }
            log.info("===============微信支付API调用成功");
            return AjaxResult.success(weChatCallBackDTO);
        } catch (ArithmeticException e) {
            log.error("金额转换异常: {}", e.getMessage());
            return AjaxResult.error("充值金额格式不正确");
        } catch (Exception e) {
            log.error("支付过程中发生异常: {}", e.getMessage(), e);
            return AjaxResult.error("系统内部错误");
        }
    }


    @Override
    public AjaxResult queryOrderState(WeChatPayDto dto) {
        try {
            // 校验商户订单号是否为空
            if (StrUtil.isBlank(dto.getOrderNumber())) {
                return AjaxResult.error("商户订单号不能为空");
            }

            // 查询订单信息
            SepRechargeOrder sepRechargeOrder = rechargeOrderService.getOne(
                    new LambdaQueryWrapper<SepRechargeOrder>()
                            .eq(SepRechargeOrder::getOrderNo, dto.getOrderNumber()));
            if (sepRechargeOrder == null) {
                return AjaxResult.error("订单信息不存在");
            }

            // 查询微信配置信息
            SepWxConfig sepWxConfig = getLatestWxConfig();
            if (sepWxConfig == null) {
                return AjaxResult.error("微信配置信息不存在");
            }

            // 查询微信支付状态
            JSONObject jsonObject = WeChatPayUtil.sendGetWeChatPay(
                    sepWxConfig.getMchId(),
                    sepWxConfig.getMchSerialNo(),
                    sepWxConfig.getApiV3Key(),
                    sepWxConfig.getPrivateKeyPath(),
                    sepRechargeOrder.getOrderNo(),
                    WeChatUrlDTO.COMMERCE_ORDER_QUERY_URL);

            if (jsonObject == null || !jsonObject.containsKey("trade_state")) {
                return AjaxResult.success("微信订单状态异常");
            }

            // 处理微信支付状态
            String tradeState = jsonObject.getString("trade_state");
            return handleTradeState(tradeState);
        } catch (Exception e) {
            // 捕获异常并返回友好提示
            return AjaxResult.error("系统异常，请稍后再试：" + e.getMessage());
        }
    }

    // 获取最新的微信配置信息
    private SepWxConfig getLatestWxConfig() {
        return sepWxConfigMapper.selectOne(
                new LambdaQueryWrapper<SepWxConfig>().orderByDesc(SepWxConfig::getId).last("LIMIT 1"));
    }

    // 处理微信支付状态
    private AjaxResult handleTradeState(String tradeState) {
        switch (tradeState) {
            case "SUCCESS":
                return AjaxResult.success("订单已支付");
            case "REFUND":
                return AjaxResult.success("订单已退款");
            case "NOTPAY":
                return AjaxResult.success("订单未支付");
            case "CLOSED":
                return AjaxResult.success("订单已关闭");
            case "REVOKED":
                return AjaxResult.success("订单已撤销（仅付款码支付会返回）");
            case "USERPAYING":
                return AjaxResult.success("用户支付中（仅付款码支付会返回）");
            case "PAYERROR":
                return AjaxResult.success("订单支付失败（仅付款码支付会返回）");
            default:
                return AjaxResult.success("微信订单状态异常");
        }
    }


    public AjaxResult refund(WeChatPayDto dto) {
        // 校验输入参数
        if (StrUtil.isBlank(dto.getOrderNumber())) {
            return AjaxResult.error("商户订单号不能为空");
        }
        if (ObjectUtil.isEmpty(dto.getAmount()) || dto.getAmount().compareTo(BigDecimal.ZERO) <= 0) {
            return AjaxResult.error("退款金额必须为正数");
        }

        try {
            // 查询订单信息
            SepRechargeOrder sepRechargeOrder = rechargeOrderService.getOne(
                    new LambdaQueryWrapper<SepRechargeOrder>()
                            .eq(SepRechargeOrder::getOrderNo, dto.getOrderNumber()));
            if (ObjectUtil.isEmpty(sepRechargeOrder)) {
                return AjaxResult.error("订单信息不存在");
            }

            // 查询微信配置信息
            SepWxConfig sepWxConfig = getLatestWxConfig();
            if (ObjectUtil.isEmpty(sepWxConfig)) {
                return AjaxResult.error("微信支付配置信息不存在");
            }

            // 订单总金额和退款金额转换为分
            int totalAmount = convertToInteger(sepRechargeOrder.getActualAmount());
            int refundAmount = convertToInteger(dto.getAmount());

            // 校验退款金额是否超过订单总金额
            if (refundAmount > totalAmount) {
                return AjaxResult.error("退款金额不能大于订单总金额");
            }

            // 生成退款记录
            String refundNum = OrderUtil.generateOrderNum(3, sepRechargeOrder.getUserId());
            SepRefundRecord sepRefundRecord = createRefundRecord(sepRechargeOrder, dto, refundNum);
            sepRefundRecordMapper.insert(sepRefundRecord);

            // 构建支付参数并发起退款
            Map<String, Object> params = buildWeChatPayParams(sepRechargeOrder, refundNum, refundAmount, totalAmount);
            JSONObject response = WeChatPayUtil.sendPostWeChatPay(
                    sepWxConfig.getMchId(),
                    sepWxConfig.getMchSerialNo(),
                    sepWxConfig.getApiV3Key(),
                    sepWxConfig.getPrivateKeyPath(),
                    JSON.toJSONString(params),
                    WeChatUrlDTO.ORDER_REFUNDS_URL);

            // 处理退款结果
            return handleRefundResponse(response, sepRefundRecord);

        } catch (NotFoundException | IOException e) {
            log.error("退款失败：{}", e.getMessage(), e);
            throw new ServiceException(e.getMessage());
        }
    }

    // 辅助方法：将金额从元转换为分
    private int convertToInteger(BigDecimal amount) {
        return amount.multiply(new BigDecimal(100)).intValueExact();
    }

    // 辅助方法：创建退款记录
    private SepRefundRecord createRefundRecord(SepRechargeOrder order, WeChatPayDto dto, String refundNum) {
        return SepRefundRecord.builder()
                .userId(order.getUserId())
                .refundChannels(1)
                .state(0)
                .orderNum(order.getOrderNo())
                .orderTransactionId(order.getTransactionId())
                .orderAmount(order.getActualAmount())
                .refundNum(refundNum)
                .refundAmount(dto.getAmount())
                .refundId(dto.getRefundId())
                .build();
    }

    // 辅助方法：构建微信支付参数
    private Map<String, Object> buildWeChatPayParams(SepRechargeOrder order, String refundNum, int refundAmount, int totalAmount) {
        LinkedHashMap<Object, Object> amountMap = new LinkedHashMap<>();
        amountMap.put("refund", refundAmount);
        amountMap.put("total", totalAmount);
        amountMap.put("currency", "CNY");

        Map<String, Object> params = new LinkedHashMap<>();
        params.put("out_trade_no", order.getOrderNo());
        params.put("out_refund_no", refundNum);
        params.put("amount", amountMap);
        return params;
    }

    // 辅助方法：处理退款响应
    private AjaxResult handleRefundResponse(JSONObject response, SepRefundRecord record) {
        String status = response.getString("status");
        String transactionId = response.getString("transaction_id");
        record.setRefundTransactionId(transactionId);

        switch (status) {
            case "SUCCESS":
            case "PROCESSING":
                record.setState(1); // 成功或处理中
                break;
            case "CLOSED":
                record.setState(2); // 已关闭
                return AjaxResult.error("当前退款已关闭");
            case "ABNORMAL":
                record.setState(2); // 异常
                return AjaxResult.error("退款异常，退款到银行发现用户的卡作废或者冻结了，" +
                        "导致原路退款银行卡失败，" +
                        "可前往商户平台-交易中心，手动处理此笔退款");
            default:
                record.setState(2); // 其他状态视为失败
                return AjaxResult.error("退款失败");
        }

        sepRefundRecordMapper.updateById(record);
        return AjaxResult.success("退款成功");
    }


    /**
     * 构建支付参数
     *
     * @param sepWxConfig 微信配置
     * @param order       自己生成订单号
     * @param amount      订单金额
     * @param openId      用户openId
     * @return 支付参数
     */
    private Map<String, Object> buildPayParams(SepWxConfig sepWxConfig,
                                               SepRechargeOrderDTO order,
                                               int amount,
                                               String openId,
                                               String ip,
                                               DeviceType deviceType) {
        Map<String, Object> params = new HashMap<>();
        params.put("mchid", sepWxConfig.getMchId());
        params.put("appid", sepWxConfig.getAppId());
        params.put("description", "充电账户充值");
        params.put("out_trade_no", order.getOrderNo());
        params.put("notify_url", sepWxConfig.getCallbackUrl());
        params.put("amount", Collections.singletonMap("total", amount));
        switch (deviceType) {
            case H5:
                params.put("scene_info", Collections.singletonMap("payer_client_ip", ip));
                break;
            case JSAPI:
                params.put("payer", Collections.singletonMap("openid", openId));
                break;
            case NATIVE:
            case APP:
                break;
        }

        return params;
    }

    /**
     * 发送微信支付请求
     *
     * @param deviceType 设备类型
     * @return 微信响应结果
     */
    private String sendWeChatPay(DeviceType deviceType) {
        switch (deviceType) {
            case JSAPI:
                log.info("---------------->微信支付API调用方式[JsApi]");
                return WeChatUrlDTO.JSAPI_PAY_URL;
            case H5:
                log.info("---------------->微信支付API调用方式[H5]");
                return WeChatUrlDTO.H5_PAY_URL;
            case NATIVE:
                log.info("---------------->微信支付API调用方式[Native]");
                return WeChatUrlDTO.NATIVE_PAY_URL;
            case APP:
                log.info("---------------->微信支付API调用方式[APP]");
                return WeChatUrlDTO.APP_PAY_URL;
            default:
                throw new ServiceException("不支持的设备类型");
        }
    }

    /**
     * 处理支付响应
     *
     * @param jsonObject  支付响应体
     * @param deviceType  支付类型
     * @param sepWxConfig 微信配置
     * @param orderNo     自己生成的订单号
     * @return 微信回调对象
     */
    private WeChatCallBackDTO handlePaymentResponse(JSONObject jsonObject,
                                                    DeviceType deviceType,
                                                    SepWxConfig sepWxConfig,
                                                    String orderNo) {
        WeChatCallBackDTO weChatCallBackDTO = new WeChatCallBackDTO();
        weChatCallBackDTO.setOrderNumber(orderNo);
        weChatCallBackDTO.setAppId(sepWxConfig.getAppId());
        weChatCallBackDTO.setPartnerId(sepWxConfig.getMchId());

        switch (deviceType) {
            case H5:
                String h5Url = jsonObject.getString("h5_url");
                if (StrUtil.isBlank(h5Url)) {
                    log.error("H5支付失败: code={}, message={}",
                            jsonObject.getString("code"),
                            jsonObject.getString("message"));
                    return null;
                }
                weChatCallBackDTO.setType(WeChatPayType.H5.getType());
                weChatCallBackDTO.setUrl(h5Url);
                break;
            case JSAPI:
                String prepayId = jsonObject.getString("prepay_id");
                if (StrUtil.isBlank(prepayId)) {
                    log.error("JSAPI支付失败: code={}, message={}",
                            jsonObject.getString("code"),
                            jsonObject.getString("message"));
                    return null;
                }
                weChatCallBackDTO.setType(WeChatPayType.JSAPI.getType());
                weChatCallBackDTO.setPrepayId("prepay_id=" + prepayId);
                weChatCallBackDTO.setNonceStr(WeChatPayUtil.generateNonceStr());
                weChatCallBackDTO.setSignType("RSA");
                weChatCallBackDTO.setPaySign(generatePaySign(weChatCallBackDTO, sepWxConfig.getPrivateKeyPath()));
                break;
            case NATIVE:
                String nativeUrl = jsonObject.getString("code_url");
                if (StrUtil.isBlank(nativeUrl)) {
                    log.error("NATIVE支付失败: code={}, message={}",
                            jsonObject.getString("code"),
                            jsonObject.getString("message"));
                    return null;
                }
                weChatCallBackDTO.setType(WeChatPayType.NATIVE.getType());
                weChatCallBackDTO.setUrl(nativeUrl);
                break;
            case APP:
                String payId = jsonObject.getString("prepay_id");
                if (StrUtil.isBlank(payId)) {
                    log.error("APP支付失败: code={}, message={}",
                            jsonObject.getString("code"),
                            jsonObject.getString("message"));
                    return null;
                }
                weChatCallBackDTO.setType(WeChatPayType.JSAPI.getType());
                weChatCallBackDTO.setPrepayId(payId);
                weChatCallBackDTO.setPackageValue("Sign=WXPay");
                weChatCallBackDTO.setNonceStr(WeChatPayUtil.generateNonceStr());
                weChatCallBackDTO.setPaySign(generatePaySign(weChatCallBackDTO, sepWxConfig.getPrivateKeyPath()));
        }
        return weChatCallBackDTO;
    }

    /**
     * 生成支付签名
     *
     * @param weChatCallBackDTO 回调信息
     * @param privateKeyPath    商户秘钥地址
     * @return 支付签名
     */
    private String generatePaySign(WeChatCallBackDTO weChatCallBackDTO, String privateKeyPath) {
        String signatureStr = String.join("\n",
                weChatCallBackDTO.getAppId(),
                weChatCallBackDTO.getTimeStamp().toString(),
                weChatCallBackDTO.getNonceStr(),
                weChatCallBackDTO.getPrepayId()
        ) + "\n";
        return WeChatPayUtil.getSign(signatureStr, privateKeyPath);
    }
}
